﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace HtmlHelper
{
    public class XhtmlElement
    {
        private string indent = "";

        private string f_elementName = "";
        public string ElementName
        {
            get { return f_elementName; }
            private set { f_elementName = value.ToLower().Trim(); }
        }
        
        private XhtmlAttributeCollection f_attributes = null;
        public XhtmlAttributeCollection Attributes
        {
            get { return f_attributes; }
        }

        private XhtmlElement f_parent=null;
        public XhtmlElement Parent
        {
            get { return f_parent; }
            set
            {
                this.f_parent = value;
                if (f_parent != null)
                {
                    this.indent = this.f_parent.indent + "\t";
                    this.f_parent.f_childs.Add(this);
                }
            }
        }


        private XhtmlElementCollection f_childs = new XhtmlElementCollection();
        public XhtmlElementCollection Childs
        {
            get { return f_childs; }
        }
        
        private XhHtmlElementType f_htmlType=XhHtmlElementType.Tag;
        public XhHtmlElementType HtmlType
        {
            get { return f_htmlType; }
        }
        
        private string f_text = "";
        public string Text
        {
            get { return f_text; }
        }
        public void SetText(string text, bool escape)
        {
            this.f_text = text;
            if (this.f_htmlType == XhHtmlElementType.Text)
            {
                this.f_text = Regex.Replace(this.f_text, @"\s+", " ");
            }
            if (escape)
            {
                
            }
            this.normalizeAndConstructHtml();
        }

        private string html;
        public string Html
        {
            get
            {
                return html;
            }
            set
            {
                string s = this.Prepare(value);
                XhtmlElement CurrentElement = this;
                string[] elements = XhtmlRegex.SpliteHtmlElement(s);
                for (int i = 0; i < elements.Length; i++)
                {
                    if (CurrentElement != null)
                    {
                        if (elements[i].StartsWith("</"))
                        {
                            string name = XhtmlRegex.getTagName(elements[i]).ToLower();
                            XhtmlElement mathedPairElement = CurrentElement;
                            while (mathedPairElement != null && mathedPairElement.ElementName != name)
                            {
                                mathedPairElement = mathedPairElement.Parent;
                            }
                            if (mathedPairElement != null)
                            {
                                CurrentElement = mathedPairElement.Parent;
                            }
                        }
                        else if (i == 0)
                        {
                            if (elements[i].StartsWith("<!--"))
                            {
                                CurrentElement.f_htmlType = XhHtmlElementType.Comment;
                                CurrentElement.ElementName = "!--";
                                CurrentElement.SetText(elements[i], false); ;
                                CurrentElement.f_attributes.Clear();
                                CurrentElement.f_childs.Clear();
                                break;
                            }
                            else if (elements[i].ToLower().StartsWith("<!doctype"))
                            {
                                CurrentElement.f_htmlType = XhHtmlElementType.Comment;
                                CurrentElement.ElementName = "!DOCTYPE";
                                CurrentElement.SetText(elements[i], false); ;
                                CurrentElement.f_attributes.Clear();
                                CurrentElement.f_childs.Clear();
                                break;
                            }
                            else if (elements[i].ToLower().StartsWith("<script"))
                            {
                                CurrentElement.f_htmlType = XhHtmlElementType.Script;
                                CurrentElement.ElementName = "script";
                                //CurrentElement.attributes = new XhtmlAttributeCollection(elements[i]);
                                CurrentElement.f_text = XhtmlRegex.geScriptText(elements[i]);
                                break;
                            }
                            else if (elements[i].StartsWith("<"))
                            {
                                CurrentElement.f_htmlType = XhHtmlElementType.Tag;
                                CurrentElement.ElementName = XhtmlRegex.getTagName(elements[i]).ToLower();
                                //CurrentElement.attributes = new XhtmlAttributeCollection(elements[i]);
                                CurrentElement.f_childs.Clear();
                            }
                            else
                            {
                                CurrentElement.f_htmlType = XhHtmlElementType.Text;
                                CurrentElement.ElementName = "";
                                CurrentElement.f_attributes.Clear();
                                CurrentElement.f_childs.Clear();
                                CurrentElement.f_text = value;
                                break;
                            }
                        }
                        else
                        {
                            XhtmlElement element = new XhtmlElement(elements[i]);
                            CurrentElement = element;
                            if (XhtmlDocument.singleTags.Contains(CurrentElement.ElementName) || CurrentElement.f_htmlType != XhHtmlElementType.Tag)
                            {
                                CurrentElement = CurrentElement.Parent;
                            }
                        }
                    }
                }
                this.normalizeAndConstructHtml();
            }
        }

        private string Prepare(string value)
        {
            value = Regex.Replace(value, @"<\s+", "<");
            value = Regex.Replace(value, @"\s+>", ">");
            value = Regex.Replace(value, @"\s*/\s*", "/");
            value = Regex.Replace(value, @">\s+<", "><");
            return value;
        }
        public XhtmlElement()
        {
            this.f_attributes = new XhtmlAttributeCollection("");
            this.f_attributes.owner = this;
        }
        public XhtmlElement(string name, XhtmlAttributeCollection attributes):this()
        {
            this.f_elementName = name;
            this.f_attributes = attributes;
            this.normalizeAndConstructHtml();
        }
        public XhtmlElement(string html):this()
        {
            this.Html = html;
        }


        public void element_HtmlChanged(object sender, EventArgs e)
        {
            this.normalizeAndConstructHtml();
        }
        private void normalizeAndConstructHtml()
        {
            this.ElementName = ElementName.ToLower().Trim();
            this.f_text = Regex.Replace(this.f_text, @"\s+", " ");
           
            string s = "";
            if (this.f_htmlType == XhHtmlElementType.Comment || this.f_htmlType == XhHtmlElementType.DocType || this.f_htmlType == XhHtmlElementType.Text)
            {
                s = this.indent + this.f_text.Replace("\r\n", "\r\n" + this.indent).Replace("\n", "\n" + this.indent);
            }
            else if (this.f_htmlType == XhHtmlElementType.Script)
            {
                s = this.indent + "<script";
                if (this.f_attributes.Html != "")
                {
                    s += " " + this.f_attributes.Html;
                }
                s += ">\r\n" + this.indent + "\t" + this.f_text.Replace("\r\n", "\r\n" + this.indent + "\t").Replace("\n", "\n" + this.indent + "\t") + "\r\n" + this.indent + "</script>";
            }
            else if (this.f_htmlType == XhHtmlElementType.Tag)
            {
                s = this.indent + "<" + this.ElementName;
                if (this.f_attributes.Html != "")
                {
                    s += " " + this.f_attributes.Html;
                }
                if (XhtmlDocument.singleTags.Contains(this.ElementName))
                {
                    s += " />";
                }
                else
                {
                    s += ">";
                    s += "\r\n" + this.f_childs.Html;
                    s += "\r\n" + this.indent + "</" + this.ElementName + ">";
                }

            }
            this.html = s;
            this.OnHtmlChanged();
        }
        public event EventHandler HtmlChanged;
        protected void OnHtmlChanged()
        {
            if (this.HtmlChanged != null)
            {
                this.HtmlChanged(this, new EventArgs());
            }
        }


        private bool f_isDirty = true;
        public bool IsDirty
        {
            get
            {
                return f_isDirty;
            }
            set
            {
                f_isDirty = value;
                this.f_parent.IsDirty = true;
                this.f_Document.IsDirty = true;
            }
        }


        private XhtmlDocument f_Document = null;

        public XhtmlDocument Document
        {
            get { return f_Document; }
            set
            {
                f_Document = value;
                if (this.f_Document != null)
                {
                    this.f_parent.IsDirty = true;
                    this.f_Document.IsDirty = true;
                }
            }
        }
    }
}
